package vmcs.controller;

// Copyright 1997 ISS.
// The contents contained in this document may not be reproduced in any
// form or by any means, without the written permission of ISS, other
// than for the purpose for which it has been supplied.
//
// Author: Olivo Miotto
//

import java.awt.Frame;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import vmcs.boundary.MachinerySimulatorPanel;
import vmcs.boundary.MessageDialog;
import vmcs.boundary.SimulatorControlPanel;
import vmcs.boundary.StoreViewer;
import vmcs.controller.statePattern.TransactionController;
import vmcs.entity.CashStore;
import vmcs.entity.CashStoreItem;
import vmcs.entity.Coin;
import vmcs.entity.Door;
import vmcs.entity.DrinksBrand;
import vmcs.entity.DrinksStore;
import vmcs.entity.DrinksStoreItem;
import vmcs.entity.Store;
import vmcs.entity.StoreItem;
import vmcs.entity.StoreObject;
import vmcs.factory.PropertyLoaderFactory;
import vmcs.factory.PropertyLoaderType;
import vmcs.observer.StoreItemChangeManager;
import vmcs.observer.StoreItemChangeManagerRegistry;
import vmcs.observer.StoreItemObserver;
import vmcs.utility.Environment;
import vmcs.utility.PropertyLoader;
import vmcs.utility.VMCSException;

public class StoreController implements StoreItemObserver {

    public final static char CASH = 'a';
    public final static char DRINK = 'b';
    private CashStore cStore;
    private DrinksStore dStore;
    private MachinerySimulatorPanel ml;
    private Door door;
    private PropertyLoader cpp;
    private PropertyLoader dpp;
    public MainController mainCtrl;
    private Map<StoreItem, Integer> itemEventMap = new HashMap< StoreItem, Integer>();

    public StoreController(MainController mctrl) {
        mainCtrl = mctrl;

        // OBSERVER IMPL BEGIN

        // get change manager for drink store item
        StoreItemChangeManager drinkMgr = StoreItemChangeManagerRegistry.getManager(StoreItemChangeManagerRegistry.StoreItemType.Drink);
        // register itself for updates
        drinkMgr.register(this, StoreItemChangeManager.EVENT_QUANTITYDRINKCHANGE);
        drinkMgr.register(this, StoreItemChangeManager.EVENT_DRINKVALUECHANGE);
        StoreItemChangeManager cointMgr = StoreItemChangeManagerRegistry.getManager(StoreItemChangeManagerRegistry.StoreItemType.Cash);
        cointMgr.register(this, StoreItemChangeManager.EVENT_QUANTITYCOINCHANGE);
        // OBSERVER IMPL END
    }

    public void initialize()
            throws VMCSException {
        try {
            door = new Door();
            cStore = new CashStore();
            dStore = new DrinksStore();
            initializeStores();
        } catch (VMCSException e) {
            throw new VMCSException("StoreController.initialize", "Error in initialize store controller" + e);
        }
    }

    public MainController getMainController() {
        return mainCtrl;
    }

    public void initializeStores()
            throws VMCSException {
        try {

            initializeCashStore();
            initializeDrinkStore();
        } catch (VMCSException e) {
            throw new VMCSException("StoreController.initializeStores", "Error in initialize cash store" + e);
        }
    }

    public void initializeDrinkStore()
            throws VMCSException {
        try {
            // get the drink file from the environment property file;
        	
            dpp = PropertyLoaderFactory.createPropertyLoader(PropertyLoaderType.DRINK, Environment.getDrinkPropFile());
            dpp.initialize();
            int numOfItems;

            numOfItems = dpp.getNumOfItems();
            dStore.setStoreSize(numOfItems);
            int i;
            StoreItem sItem;

            for (i = 0; i < numOfItems; i++) {
                sItem = dpp.getItem(i + 1);
                // add drink item in this function;
                addDrinksItem(i, sItem);
            }


        } catch (Exception e) {
            throw new VMCSException("StoreController.initializeDrinkStore", "Error in initialize drink store " + e);
        }
    }

    public void initializeCashStore()
            throws VMCSException {
        try {
            // get the cash file from the environment property file;
            PropertyLoader prop = PropertyLoaderFactory.createPropertyLoader(PropertyLoaderType.CASH, Environment.getCashPropFile());
            cpp = prop;
            prop.initialize();
            int numOfItems;

            numOfItems = prop.getNumOfItems();
            cStore.setStoreSize(numOfItems);
            int i;
            StoreItem sItem;

            for (i = 0; i < numOfItems; i++) {
                sItem = prop.getItem(i + 1);

                // add cash item in this function;
                cStore.addItem(i, sItem);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new VMCSException("StoreController.initializeCashStore", "Error in initialize cash store " + e);
        }
    }

    public void addDrinksItem(int idx, StoreItem item)
            throws VMCSException {
        StoreObject ob;   // existed object
        StoreObject ob1;  // the new object

        ob1 = item.getContent();
        ob = dStore.findObject(ob1.getName());

        // check for the consistency of brand price;
        if (ob != null) {
            DrinksBrand bd;
            DrinksBrand bd1;
            bd = (DrinksBrand) ob;
            bd1 = (DrinksBrand) ob1;
            if (bd.getPrice() != bd1.getPrice()) {
                // the new price is not the same as that of the prievous item with the same name;
                throw new VMCSException("StoreController.addDrinksItem", "Inconsist brand price");
            }
        }
        if (ob == null) {
            dStore.addItem(idx, item);
            return;
        }

        item.setContent(ob);

        dStore.addItem(idx, item);
    }

    public MachinerySimulatorPanel getMachinerySimulatorPanel() {
        return ml;
    }

    public void displayMachineryPanel() {
        SimulatorControlPanel scp = mainCtrl.getSimulatorControlPanel();
        if (ml == null) {
            ml = new MachinerySimulatorPanel((Frame) scp, this);
        }

        ml.display();

        System.out.println("get door status:" + door.isDoorClosed());


        scp.setActive(SimulatorControlPanel.ACT_MACHINERY, false);
    }

    public void closeMachineryPanel() {
        if (ml == null) {
            return;
        }
        boolean ds = isDoorClosed();

        if (ds == false) {
            MessageDialog msg = new MessageDialog(ml, "Please Lock the Door before You Leave");
            return;
        }
        ml.dispose();
        SimulatorControlPanel scp = mainCtrl.getSimulatorControlPanel();
        scp.setActive(SimulatorControlPanel.ACT_MACHINERY, true);
        return;
    }

    public int getStoreSize(char type) {
        if (type == StoreController.CASH) {
            return cStore.getStoreSize();
        } else {
            return dStore.getStoreSize();
        }
    }

    public StoreItem[] getStoreItems(char type) {
        if (type == StoreController.CASH) {
            return cStore.getItems();
        } else {
            return dStore.getItems();
        }
    }

    public void changeStoreQty(char type, int idx, int qty) {
        try {
            System.out.println("StoreController.changeStoreQty: type:" + type + "   qty:" + qty);
            if (type == StoreController.CASH) {
                cStore.setQuantity(idx, qty);
            } else {
                dStore.setQuantity(idx, qty);
            }
            // to automatically change the displayed stock quantitiy.
            // not required in the requirement.
            MaintenanceController mctrl;
            mctrl = mainCtrl.getMaintenanceController();
            mctrl.changeStoreQty(type, idx, qty);

        } catch (VMCSException e) {
            System.out.println("StoreController.changeStoreQty:" + e);
        }
    }

    public StoreItem getStoreItem(char type, int idx)
            throws VMCSException {
        if (type == StoreController.CASH) {
            return cStore.getStoreItem(idx);
        } else {
            return dStore.getStoreItem(idx);
        }
    }

    public void setPrice(int idx, int pr)
            throws VMCSException {
        DrinksStoreItem item;
        item = (DrinksStoreItem) dStore.getStoreItem(idx);
        item.setPrice(pr);
    }

    public int getTotalCash()
            throws VMCSException {
        int i;
        int size;

        size = cStore.getStoreSize();
        CashStoreItem item;
        int qty;
        int val;
        int tc = 0;
        Coin c;

        for (i = 0; i < size; i++) {
            item = (CashStoreItem) cStore.getStoreItem(i);
            qty = item.getQuantity();
            c = (Coin) item.getContent();
            val = c.getValue();
            tc = tc + qty * val;
        }
        return tc;
    }

    public int transferAll()
            throws VMCSException {
        int i;
        int cc = 0;  // coin quauntity;
        int size = cStore.getStoreSize();

        CashStoreItem item;
        for (i = 0; i < size; i++) {
            item = (CashStoreItem) cStore.getStoreItem(i);
            cc = cc + item.getQuantity();
            item.setQuantity(0);
        }

        return cc;
    }

    public boolean isDoorClosed() {
        return door.isDoorClosed();
    }

    public void closeDown()
            throws VMCSException {
        // save back cash property;
        saveCashProperty();
        saveDrinksProperty();
        if (ml != null) {
            ml.dispose();
        }
    }

    public void saveCashProperty()
            throws VMCSException {
        int size;

        size = cStore.getStoreSize();

        int i;
        cpp.setNumOfItems(size);

        for (i = 0; i < size; i++) {
            cpp.setItem(i, (CashStoreItem) cStore.getStoreItem(i));
        }

        cpp.saveProperty();
    }

    /**
     * save the drink property when simulation is ended.
     */
    public void saveDrinksProperty()
            throws VMCSException {
        int size;

        size = dStore.getStoreSize();
        cpp.setNumOfItems(size);

        int i;
        for (i = 0; i < size; i++) {
            dpp.setItem(i, (DrinksStoreItem) dStore.getStoreItem(i));
        }

        dpp.saveProperty();
    }

    public void setDoorState(boolean state) {
        System.out.println("StoreController.setDoorState:" + state);
        door.setState(state);
        displayDoorState();
    }

    public void displayDoorState() {
        if (ml == null) {
            return;
        }

        boolean state = door.isDoorClosed();

        ml.setDoorState(state);

    }

    // update drink stock view;
    public void displayDrinkStock()
            throws VMCSException {
        if (ml == null) {
            return;
        }

        int i, qty;
        DrinksStoreItem item;
        int size;
        size = this.getStoreSize(StoreController.DRINK);

        for (i = 0; i < size; i++) {
            item = (DrinksStoreItem) this.getStoreItem(StoreController.DRINK, i);

            ml.getDrinksStoreDisplay().update(i, item.getQuantity());
        }

    }

    // update coin stock view after transfer all cash;
    public void displayCoinStock()
            throws VMCSException {
        if (ml == null) {
            return;
        }

        int i, qty;
        CashStoreItem item;
        int size;
        size = this.getStoreSize(StoreController.CASH);

        for (i = 0; i < size; i++) {
            item = (CashStoreItem) this.getStoreItem(StoreController.CASH, i);

            ml.getCashStoreDisplay().update(i, item.getQuantity());
        }
    }

    public Store getStore(char type) {
        if (type == StoreController.CASH) {
            return (Store) cStore;
        } else {
            return (Store) dStore;
        }
    }

    // OBSERVER IMPL BEGIN
	/* (non-Javadoc)
     * @see vmcs.observer.StoreItemObserver#update(vmcs.entity.StoreItem, int)
     */
    @Override
    public void update(StoreItem storeItem, int event) {
        int i, qty, size;
        if( (!TransactionController.instance().getMaintenanceFlag() && ( ml != null)) ){
            size = this.getStoreSize(StoreController.CASH);
            if (event == StoreItemChangeManager.EVENT_QUANTITYCOINCHANGE) {
                for (i = 0; i < size; i++) {
                    try {
                        CashStoreItem item = (CashStoreItem) this.getStoreItem(StoreController.CASH, i);
                        if( item == storeItem){
                            StoreViewer storeVw = ml.getCashStoreDisplay();
                            if(storeVw != null){
                                storeVw.update(i, item.getQuantity());
                            }
                            MaintenanceController maint = mainCtrl.getMaintenanceController();
                            if( (maint != null) && (maint.getMaintenancePanel()!=null) ){
                                maint.changeStoreQty('a', i, item.getQuantity());
                            }
                        }
                    } catch (VMCSException ex) {
                        Logger.getLogger(StoreController.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            } else if (event == StoreItemChangeManager.EVENT_QUANTITYDRINKCHANGE) {
                DrinksStoreItem item;
                size = this.getStoreSize(StoreController.DRINK);
                for (i = 0; i < size; i++) {
                    try {
                        item = (DrinksStoreItem) this.getStoreItem(StoreController.DRINK, i);
                        if( item == storeItem ) {
                            StoreViewer storeVw = ml.getDrinksStoreDisplay();
                            if(storeVw != null){
                                storeVw.update(i, item.getQuantity());
                            }
                            MaintenanceController maint = mainCtrl.getMaintenanceController();
                            if( (maint != null) && (maint.getMaintenancePanel()!=null) )
                                maint.changeStoreQty('b', i, item.getQuantity());
                        }
                    } catch (VMCSException ex) {
                        Logger.getLogger(StoreController.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                
            } else if (event == StoreItemChangeManager.EVENT_DRINKVALUECHANGE) {
                System.out.println("Change of drink price");
            } else {
                throw new ArrayIndexOutOfBoundsException("Invalid type passed: " + event);
            }
        }
    }
    // OBSERVER IMPL END

    //Chain of responsibility
    public StoreItem getCoinStoreItem(double weight) {
        return cStore.getCoinItem(weight);
    }

    public StoreItem getDrinkStoreItem(String brand) {
        return dStore.getDrinkItem(brand);
    }
    //Chain of responsibility end

}
